/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
 *
 * Copyright © 2007 Mozilla Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it either under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * (the "LGPL") or, at your option, under the terms of the Mozilla
 * Public License Version 1.1 (the "MPL"). If you do not alter this
 * notice, a recipient may use your version of this file under either
 * the MPL or the LGPL.
 *
 * You should have received a copy of the LGPL along with this library
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * You should have received a copy of the MPL along with this library
 * in the file COPYING-MPL-1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
 * the specific language governing rights and limitations.
 *
 * The Original Code is the cairo graphics library.
 *
 * The Initial Developer of the Original Code is Mozilla Corporation.
 *
 * Contributor(s):
 *	Vladimir Vukicevic <vladimir@mozilla.com>
 */

#include "cairoint.h"

#include "cairo-skia.h"
#include "cairo-skia-private.h"

#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-fallback-private.h"

static cairo_skia_surface_t *
_cairo_skia_surface_create_internal (SkBitmap::Config config,
				     bool opaque,
				     unsigned char *data,
				     int width,
				     int height,
				     int stride);

static cairo_surface_t *
_cairo_skia_surface_create_similar (void *asurface,
				    cairo_content_t content,
				    int width,
				    int height)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
    SkBitmap::Config config;
    bool opaque;

    if (content == surface->image.base.content)
    {
	config = surface->bitmap->config ();
	opaque = surface->bitmap->isOpaque ();
    }
    else if (! format_to_sk_config (_cairo_format_from_content (content),
				    config, opaque))
    {
	return NULL;
    }

    return &_cairo_skia_surface_create_internal (config, opaque,
						 NULL,
						 width, height,
						 0)->image.base;
}

static cairo_status_t
_cairo_skia_surface_finish (void *asurface)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;

    cairo_surface_finish (&surface->image.base);
    delete surface->bitmap;

    return CAIRO_STATUS_SUCCESS;
}

static cairo_image_surface_t *
_cairo_skia_surface_map_to_image (void *asurface,
				  const cairo_rectangle_int_t *extents)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;

    surface->bitmap->lockPixels ();
    return _cairo_image_surface_map_to_image (&surface->image, extents);
}

static cairo_int_status_t
_cairo_skia_surface_unmap_image (void *asurface,
				 cairo_image_surface_t *image)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
    cairo_int_status_t status;

    status = _cairo_image_surface_unmap_image (&surface->image, image);
    surface->bitmap->unlockPixels ();

    return status;
}

static cairo_status_t
_cairo_skia_surface_acquire_source_image (void *asurface,
					  cairo_image_surface_t **image_out,
					  void **image_extra)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;

    surface->bitmap->lockPixels ();

    *image_out = &surface->image;
    *image_extra = NULL;
    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_skia_surface_release_source_image (void *asurface,
					  cairo_image_surface_t *image,
					  void *image_extra)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;

    surface->bitmap->unlockPixels ();
}

static cairo_bool_t
_cairo_skia_surface_get_extents (void *asurface,
				  cairo_rectangle_int_t *extents)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface;
    extents->x = extents->y = 0;
    extents->width  = surface->image.width;
    extents->height = surface->image.height;
    return TRUE;
}

static void
_cairo_skia_surface_get_font_options (void                  *abstract_surface,
				       cairo_font_options_t  *options)
{
    _cairo_font_options_init_default (options);

    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
}

static const struct _cairo_surface_backend
cairo_skia_surface_backend = {
    CAIRO_SURFACE_TYPE_SKIA,
    _cairo_skia_surface_finish,

    _cairo_skia_context_create,

    _cairo_skia_surface_create_similar,
    NULL, //_cairo_skia_surface_create_similar_image,
    _cairo_skia_surface_map_to_image,
    _cairo_skia_surface_unmap_image,

    _cairo_surface_default_source,
    _cairo_skia_surface_acquire_source_image,
    _cairo_skia_surface_release_source_image,
    NULL, /* snapshot */

    NULL, /* copy_page */
    NULL, /* show_page */

    _cairo_skia_surface_get_extents,
    _cairo_skia_surface_get_font_options,

    NULL, /* flush */
    NULL, /* mark_dirty_rectangle */

    /* XXX native surface functions? */
    _cairo_surface_fallback_paint,
    _cairo_surface_fallback_mask,
    _cairo_surface_fallback_stroke,
    _cairo_surface_fallback_fill,
    NULL, /* fill/stroke */
    _cairo_surface_fallback_glyphs
};

/*
 * Surface constructors
 */

static inline pixman_format_code_t
sk_config_to_pixman_format_code (SkBitmap::Config config,
				 bool opaque)
{
    switch (config) {
    case SkBitmap::kARGB_8888_Config:
	return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8;

    case SkBitmap::kA8_Config:
	return PIXMAN_a8;
    case SkBitmap::kRGB_565_Config:
	return PIXMAN_r5g6b5;
    case SkBitmap::kARGB_4444_Config:
	return PIXMAN_a4r4g4b4;

    case SkBitmap::kNo_Config:
    case SkBitmap::kIndex8_Config:
    default:
	ASSERT_NOT_REACHED;
	return (pixman_format_code_t) -1;
    }
}

static cairo_skia_surface_t *
_cairo_skia_surface_create_internal (SkBitmap::Config config,
				     bool opaque,
				     unsigned char *data,
				     int width,
				     int height,
				     int stride)
{
    cairo_skia_surface_t *surface;
    pixman_image_t *pixman_image;
    pixman_format_code_t pixman_format;
    SkColorType colorType;

    surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t));
    if (unlikely (surface == NULL))
	return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));

    pixman_format = sk_config_to_pixman_format_code (config, opaque);
    pixman_image = pixman_image_create_bits (pixman_format,
					     width, height,
					     (uint32_t *) data, stride);
    if (unlikely (pixman_image == NULL)) {
	free (surface);
	return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
    }

    _cairo_surface_init (&surface->image.base,
			 &cairo_skia_surface_backend,
			 NULL, /* device */
			 _cairo_content_from_pixman_format (pixman_format));

    _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);

    surface->bitmap = new SkBitmap;
    colorType = SkBitmapConfigToColorType(config);
    surface->bitmap->setAlphaType (opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
    surface->bitmap->setInfo (SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType), surface->image.stride);
    surface->bitmap->setPixels (surface->image.data);

    surface->image.base.is_clear = data == NULL;

    return surface;
}

cairo_surface_t *
cairo_skia_surface_create (cairo_format_t format,
			   int width,
			   int height)
{
    SkBitmap::Config config;
    bool opaque;

    if (! CAIRO_FORMAT_VALID (format) ||
	! format_to_sk_config (format, config, opaque))
    {
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
    }

    return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base;
}

cairo_surface_t *
cairo_skia_surface_create_for_data (unsigned char *data,
				    cairo_format_t format,
				    int width,
				    int height,
				    int stride)
{
    SkBitmap::Config config;
    bool opaque;

    if (! CAIRO_FORMAT_VALID (format) ||
	! format_to_sk_config (format, config, opaque))
    {
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
    }

    return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base;
}

/***

Todo:

*** Skia:

- mask()

*** Sk:

High:
- antialiased clipping?

Medium:
- implement clip path reset (to avoid restore/save)
- implement complex radial patterns (2 centers and 2 radii)

Low:
- implement EXTEND_NONE

***/
